package coupon

import (
	"context"
	"math/rand"
	"sale-admin/config/redis"
	"sale-admin/internal/app/web/service/common"
	"sale-admin/internal/dal"
	"sale-admin/internal/dao"
	"sale-admin/internal/define"
	"sale-admin/internal/models"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/logx"
	"github.com/go-playground/validator/v10"
	"github.com/samber/lo"
)

type CouponBatch struct{}

// AddBatch 新增优惠码批次
func (CouponBatch) Add(
	ctx context.Context,
	name string,
	code string,
	codeType int,
	totalNum int64,
	totalTimes int64, // 每个码最大使用次数，-1为不限制
	validityStartTime int64, // 有效期开始时间
	validityEndTime int64, // 有效期结束时间
	orderAmountRequired int, // 是否需要满足订单金额，才能使用
	orderAmount, // 要满足的订单金额
	reduce float64, // 满减的额度
	discount float64, // 打折的额度
	remark string, // 备注
	desc string, // 详情描述
	merchantID string, // 商户id
	allowGoodsIDs []string, // 可使用的商品
	allowAreaIDs []string, // 可使用的区域
	allowRobotIDs []string, // 可使用的机器
) (id string, err error) {
	if !lo.Contains([]int{1, 2, 3}, codeType) {
		return "", errorx.New("无效优惠码类型", -1)
	}
	if codeType == 1 && code == "" {
		return "", errorx.New("通用码格式无效", -1)
	}
	if codeType == 1 {
		validate := validator.New()
		if err := validate.Var(code, "gte=6,lte=10"); err != nil {
			return "", errorx.New("通用码的长度必须为6-16位", -1)
		}
		if err := validate.Var(code, "alphanum"); err != nil {
			return "", errorx.New("通用码只能包含数字或字母", -1)
		}
	}
	if codeType == 2 && totalTimes == -1 {
		return "", errorx.New("一人一码必须指定使用次数", -1)
	}
	if discount >= 10 {
		return "", errorx.New("无效的折扣", -1)
	}
	if discount == 0 {
		discount = 9.9
	}
	if totalNum <= 0 {
		totalNum = 1
	}
	if totalNum > 10000 {
		return "", errorx.New("发放总量不能超过10000", -1)
	}
	// 通用的发放量固定为1
	if codeType == 1 {
		totalNum = 1
	}
	// 每个优惠码最小使用次数默认1次
	if totalTimes <= 0 {
		totalTimes = 1
	}
	if validityStartTime == 0 || validityEndTime == 0 {
		return "", errorx.New("有效期限不能为空", -1)
	}
	if validityEndTime < validityStartTime || validityEndTime < time.Now().Unix() {
		return "", errorx.New("有效期限不合法", -1)
	}
	// 兑换码只能使用导流商品
	if codeType == 3 {
		for _, goodsID := range allowGoodsIDs {
			goodsDetail, _ := dao.Goods{}.Detail(ctx, dal.Q, goodsID)
			if goodsDetail == nil || goodsDetail.GoodsType != 2 {
				return "", errorx.New("兑换码只能使用导流商品", -1)
			}
		}
	}
	var record = &models.CouponBatch{
		ID:                  "",
		Name:                name,
		Code:                code,
		CodeType:            codeType,
		Status:              int(define.CouponBatchStatus_Valid), // 默认有效
		TotalNum:            0,                                   // 添加批次时，发放量先为0。在码库生成后，自动更新
		TotalTimes:          totalTimes,
		ValidityStartTime:   validityStartTime,
		ValidityEndTime:     validityEndTime,
		OrderAmountRequired: orderAmountRequired,
		OrderAmount:         orderAmount,
		Reduce:              reduce,
		Discount:            discount,
		MerchantID:          merchantID,
		Remark:              remark,
		Desc:                desc,
	}
	// 创建批次
	batchID, err := dao.CouponBatch{}.Create(ctx, dal.Q, record)
	if err != nil {
		return "", errorx.New("新增批次失败", -1)
	}
	// 创建关联商品
	{
		records := []*models.MaObjAssociation{}
		for _, goodsID := range allowGoodsIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponGoods),
				ObjID:           batchID,
				AssociationID:   goodsID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 创建关联区域
	{
		records := []*models.MaObjAssociation{}
		for _, areaID := range allowAreaIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponArea),
				ObjID:           batchID,
				AssociationID:   areaID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 创建关联设备
	{
		records := []*models.MaObjAssociation{}
		for _, robotID := range allowRobotIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponRobot),
				ObjID:           batchID,
				AssociationID:   robotID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 针对通用码，码的库存指的是，码的复用次数
	if codeType == 1 {
		totalNum = totalTimes
	}
	// 生成码库
	err = CouponBatch{}.AddInventory(ctx, merchantID, batchID, totalNum)
	if err != nil {
		return batchID, errorx.New(err.Error(), 0)
	}
	return batchID, nil
}

// Update 更新优惠码批次
func (CouponBatch) Update(
	ctx context.Context,
	ID, name string,
	totalTimes int64, // 每个码最大使用次数，-1为不限制
	validityStartTime int64, // 有效期开始时间
	validityEndTime int64, // 有效期结束时间
	orderAmountRequired int, // 是否需要满足订单金额，才能使用.1: 需要 2：不需要
	orderAmount, // 要满足的订单金额
	reduce, // 满减的额度
	discount float64, // 打折的额度
	remark string, // 备注
	desc string, // 详情描述
	merchantID string, // 商户id
	allowGoodsIDs []string, // 可使用的商品
	allowAreaIDs []string, // 可使用的区域
	allowRobotIDs []string, // 可使用的机器
) error {
	if !(dao.CouponBatch{}).Exists(ctx, dal.Q, ID) {
		return errorx.New("无效的批次id", -1)
	}
	if discount >= 10 {
		return errorx.New("无效的折扣", -1)
	}
	// 获取直系商户
	merchantIDs, err := common.GetFamilyMerchants(ctx, merchantID)
	if err != nil {
		logx.Error(ctx, "GetFamilyMerchants err", logx.Err(err))
		return errorx.New("更新失败", -1)
	}
	if validityStartTime == 0 || validityEndTime == 0 {
		return errorx.New("有效期限不能为空", -1)
	}
	if validityEndTime < validityStartTime || validityEndTime < time.Now().Unix() {
		return errorx.New("有效期限不合法", -1)
	}
	// 删除旧的关联
	dao.ObjAssociation{}.Delete(ctx, dal.Q, define.AssociationCouponGoods, ID, "")
	dao.ObjAssociation{}.Delete(ctx, dal.Q, define.AssociationCouponArea, ID, "")
	dao.ObjAssociation{}.Delete(ctx, dal.Q, define.AssociationCouponRobot, ID, "")
	// 创建关联商品
	{
		records := []*models.MaObjAssociation{}
		for _, goodsID := range allowGoodsIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponGoods),
				ObjID:           ID,
				AssociationID:   goodsID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 创建关联区域
	{
		records := []*models.MaObjAssociation{}
		for _, areaID := range allowAreaIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponArea),
				ObjID:           ID,
				AssociationID:   areaID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 创建关联设别
	{
		records := []*models.MaObjAssociation{}
		for _, robotID := range allowRobotIDs {
			records = append(records, &models.MaObjAssociation{
				AssociationType: int(define.AssociationCouponRobot),
				ObjID:           ID,
				AssociationID:   robotID,
			})
		}
		dao.ObjAssociation{}.Add(ctx, dal.Q, records, 300)
	}
	// 更新数据库
	dao.CouponBatch{}.Update(
		ctx, dal.Q,
		ID, name, totalTimes,
		int64(validityStartTime), int64(validityEndTime),
		orderAmountRequired, orderAmount,
		reduce, discount,
		remark, desc,
		merchantIDs,
	)
	return nil
}

// List 批次列表
func (CouponBatch) List(
	ctx context.Context,
	name string,
	codeType int,
	status int,
	merchantID string,
	page, limit int,
) (
	records []*models.CouponBatch, total int64, err error,
) {
	merchantIDs := []string{}
	if merchantID != "1" {
		// 获取直系商户
		merchantIDs, err = common.GetFamilyMerchants(ctx, merchantID)
		if err != nil {
			logx.Error(ctx, "GetFamilyMerchants err", logx.Err(err))
			return []*models.CouponBatch{}, 0, errorx.New("获取批次列表失败", -1)
		}
	}
	return dao.CouponBatch{}.List(ctx, dal.Q, name, codeType, define.StatusType(status), merchantIDs, page, limit)
}

type CounponBatchDetailT struct {
	models.CouponBatch
	AllowGoodsIDs []string `json:"allow_goods_ids"`
	AllowRobotIDs []string `json:"allow_robot_ids"`
}

// Detail 批次详情
func (CouponBatch) Detail(
	ctx context.Context,
	ID string,
) (
	detail *CounponBatchDetailT, err error,
) {
	couponBatchDetail, _ := dao.CouponBatch{}.Detail(ctx, dal.Q, ID)
	if couponBatchDetail == nil {
		return nil, errorx.New("无效的批次", -1)
	}
	// 获取绑定的商品
	goodsIDs, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationCouponGoods, couponBatchDetail.ID, "")
	if len(goodsIDs) == 0 {
		goodsIDs = []string{}
	}
	// 获取绑定的设备
	robotIDs, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationCouponRobot, couponBatchDetail.ID, "")
	if len(robotIDs) == 0 {
		robotIDs = []string{}
	}
	detail = &CounponBatchDetailT{}
	detail.CouponBatch = *couponBatchDetail
	detail.AllowGoodsIDs = goodsIDs
	detail.AllowRobotIDs = robotIDs
	return detail, nil
}

// Options 批次选项
func (CouponBatch) Options(
	ctx context.Context,
	codeType int,
	merchantID string,
) (records []dao.CountBatchOptionType) {
	return dao.CouponBatch{}.Options(ctx, dal.Q, codeType, merchantID)
}

// Disable 失效
func (CouponBatch) Invalid(
	ctx context.Context,
	merchantID string,
	batchID string,
) error {
	// 获取直系商户
	merchantIDs, err := common.GetFamilyMerchants(ctx, merchantID)
	if err != nil {
		logx.Error(ctx, "GetFamilyMerchants err", logx.Err(err))
		return errorx.New("禁用失败", -1)
	}
	if !(dao.CouponBatch{}).Exists(ctx, dal.Q, batchID) {
		return errorx.New("无效的批次id", -1)
	}
	return dao.CouponBatch{}.Invalid(ctx, dal.Q, batchID, merchantIDs)
}

// CreateInventoryTaskType 创建库存
type CreateInventoryTaskType struct {
	BatchID  string `json:"batch_id"`
	TotalNum int    `json:"total_num"`
	IsAdd    bool   `json:"is_add"`
}

// AddInventory 添加库存
// 当添加的库存是1时，返回码号
func (CouponBatch) AddInventory(
	ctx context.Context,
	merchantID string,
	batchID string,
	totalNum int64,
) error {
	// 获取码库批次信息
	var generalCode string
	batchDetail, err := CouponBatch{}.Detail(ctx, batchID)
	if err != nil || batchDetail == nil {
		return errorx.New("无效的批次", -1)
	}
	if batchDetail.Status == 2 {
		return errorx.New("批次已失效", -1)
	}
	if batchDetail.ValidityEndTime < time.Now().Unix() {
		return errorx.New("批次已失效", -1)
	}
	// 获取通用码的码号
	if batchDetail.CodeType == 1 {
		generalCode = batchDetail.Code
	}
	// 生成主码和子码
	{
		// 生成优惠码
		codeRecords := []*models.CouponCode{}
		childCodeRecords := []*models.CouponChildCode{}
		// 通用码
		if batchDetail.CodeType == 1 {
			if !(dao.CouponCode{}).Exists(ctx, dal.Q, generalCode) {
				codeRecords = append(codeRecords, &models.CouponCode{
					MerchantID: merchantID,
					BatchID:    batchID,
					Code:       generalCode,
					Status:     int(define.CouponCodeStatus_UnUsed),
					TotalTimes: batchDetail.TotalTimes,
				})
			}
			// 针对通用码，增加库存，实际增加的码的复用次数
			for j := int64(0); j < totalNum; j++ {
				childCodeRecords = append(childCodeRecords, &models.CouponChildCode{
					Code:    generalCode,
					BatchID: batchID,
					Status:  int(define.CouponChildCodeStatus_UnUsed),
				})
			}
		} else if batchDetail.CodeType == 2 || batchDetail.CodeType == 3 {
			for i := int64(0); i < totalNum; i++ {
				code := ""
				// 生成一个不重复的优惠码
				for {
					rdb := redis.NewDB()
					tempCode := genCode()
					// 增加一个并发锁，用于处理接口发码和后台生成码的并发问题
					redisCtx, redisCancel := context.WithTimeout(context.Background(), time.Second*3)
					success, _ := rdb.SetNX(redisCtx, define.RedisCouponCodeGenLock+tempCode, 1, time.Minute*10).Result()
					redisCancel()
					if !success {
						time.Sleep(time.Millisecond * 100)
						continue
					}
					if (dao.CouponCode{}).Exists(ctx, dal.Q, tempCode) {
						continue
					}
					code = tempCode
					break
				}
				// 主码
				codeRecords = append(codeRecords, &models.CouponCode{
					MerchantID: merchantID,
					BatchID:    batchID,
					Code:       code,
					Status:     int(define.CouponCodeStatus_UnUsed),
					TotalTimes: 1,
				})
				// 生成子码
				for j := int64(0); j < batchDetail.TotalTimes; j++ {
					childCodeRecords = append(childCodeRecords, &models.CouponChildCode{
						Code:    code,
						BatchID: batchID,
						Status:  int(define.CouponChildCodeStatus_UnUsed),
					})
				}
			}
		}
		// 保存主码
		{
			if len(codeRecords) > 0 {
				err := dao.CouponCode{}.Create(ctx, dal.Q, codeRecords)
				if err != nil {
					return errorx.New("码库生成失败", -1)
				}
			}
			// 保存子码
			if len(childCodeRecords) > 0 {
				err := dao.CouponChildCode{}.Create(ctx, dal.Q, childCodeRecords)
				if err != nil {
					return errorx.New("码库生成失败", -1)
				}
			}
		}
		// 更新发放量
		{
			err := dao.CouponBatch{}.UpdateTotalNum(ctx, dal.Q, batchID, totalNum)
			if err != nil {
				return errorx.New("发放量更新失败", -1)
			}
		}
	}
	return nil
}

// GoodsList 用码商品列表统计
func (CouponBatch) GoodsList(
	ctx context.Context,
	batchID, goodsName string,
	page, limit int,
) (records []dao.GoodsListType, total int64, err error) {
	if !(dao.CouponBatch{}).Exists(ctx, dal.Q, batchID) {
		return []dao.GoodsListType{}, 0, errorx.New("无效的批次", -1)
	}
	// 获取商品id列表
	goodsIDs := []string{}
	if goodsName != "" {
		goodsIDs = dao.Goods{}.GetGoodsIDByName(ctx, dal.Q, goodsName)
	}
	records, total = dao.ActivityOrder{}.GoodsList(
		ctx,
		dal.Q,
		define.ActivityOrderType_Coupon,
		batchID,
		goodsIDs,
		define.OrderStatus_Shipment_Success,
		page, limit,
	)
	// 获取商品名称，价格，图片
	if len(records) > 0 {
		goodsIDs := []string{}
		for _, record := range records {
			goodsIDs = append(goodsIDs, record.GoodsID)
		}
		goodsDetailMap := dao.Goods{}.DetailMap(ctx, dal.Q, goodsIDs)
		// 补充商品详细信息
		for index, record := range records {
			records[index].GoodsName = goodsDetailMap[record.GoodsID].GoodsName1  // 商品名称
			records[index].GoodsImage = goodsDetailMap[record.GoodsID].GoodsImage // 商品图片
			records[index].GoodsType = goodsDetailMap[record.GoodsID].GoodsType   // 商品类型
		}
	}

	return records, total, nil
}

// genCode 生成优惠码
func genCode() (code string) {
	bytes := []byte{}
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	seed := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	for i := 0; i < 6; i++ {
		bytes = append(bytes, seed[r.Intn(36)])
	}
	return string(bytes)
}
